package syncer

import (
	"encoding/json"
	"fmt"
	"gluenet/pkg/apis"
	"gluenet/pkg/meta"
	"gluenet/pkg/ops"
	"strings"
	"time"
)

func syncDag(c *apis.StorageContext) {
	var intances []meta.Instance
	if err := c.DataBase.InstanceListAll(&intances); err != nil {
		c.Logger.Errorf("query instance %v", err)
		return
	}

	var dag = make(map[string]interface{})
	for _, v := range intances {
		var irun *meta.InstanceRunTime
		if err := c.DataBase.InstanceRuntimeGet(v.Guid, &irun); err != nil {
			c.Logger.Errorf("query instance runtime %v", err)
			continue
		}

		// todo get nodes
		var nodes = make(map[string]interface{})
		if err := json.Unmarshal([]byte(irun.ContainersInfo), &nodes); err != nil {
			c.Logger.Warnf("unmarshall k8s container info  %v", err)
			continue
		}

		// todo get links
		var tracesBytes string
		if err := c.DataBase.InstanceRuntimeContainerTracesGet(v.Guid, &tracesBytes); err != nil {
			//c.Logger.Debugf("instance %v get trace %v", v.Name, err)
			continue
		}

		var g interface{}
		if tracesBytes != "" {
			var err error
			var traces = make(map[string]interface{})
			if err = json.Unmarshal([]byte(tracesBytes), &traces); err != nil {
				c.Logger.Errorf("unmarshall is nil %v", err)
				continue
			}

			g, err = daglinks(c, traces, v.Platform)

			if err != nil {
				c.Logger.Errorf("dag link err %v", err)
				continue
			}

		}

		// deal data
		dag[v.Guid] = map[string]interface{}{
			"dag_nodes": nodes,
			"dag_links": g,
		}
	}

	bts, _ := json.Marshal(dag)
	//orm2.SaveResourceDag(c, "glue", time.Now().Unix(), string(bts))
	c.DataBase.DagPut(ops.DagJaeger, &meta.Dag{
		UpdateTime:  time.Now().String(),
		InstanceDag: string(bts),
	})
}

//func syncDag(c *apis.GlueContext) {
//	var intances []meta.Instance
//	if err := c.DataBase.MetaDB().SQL("select * from instance").Find(&intances); err != nil {
//		c.Logger.Errorf("query instance %v", err)
//		return
//	}
//
//	end := time.Now().Unix()
//	start := end - 3600*24*30
//
//	var dag = make(map[string]interface{}, len(intances))
//	for _, v := range intances {
//		var traces []interface{}
//		if err := c.DataBase.GetTracesK8s("test-helm-social-network", start*1000000, end*1000000, 1, &traces); err != nil {
//			c.Logger.Errorf("get traces %v", err)
//			return
//		}
//
//		//c.Logger.Debugf("get trace dag %v", len(traces))
//
//		g, err := daglinks(c, traces)
//		if err != nil {
//			c.Logger.Errorf("draw dag %v", err)
//			return
//		}
//
//		dag[v.Guid] = g
//	}
//
//	//c.Logger.Debugf("dag %v", dag)
//
//	bts, _ := json.Marshal(dag)
//	orm.SaveResourceDag(c, "glue", time.Now().Unix(), string(bts))
//}

func daglinks(c *apis.StorageContext, traces map[string]interface{}, platfrom string) ([]interface{}, error) {
	//var dagNodes = make(map[string]interface{})
	var dagLinksMap = make(map[string]map[string]int)
	var dagLinksMapT = make(map[string]map[string][]string)
	var dagLinks = []interface{}{}
	for id, v := range traces {
		//fmt.Printf("\n %v \n", v)

		pro := v.(map[string]interface{})["processes"].(map[string]interface{})

		var ptn = make(map[string]string, len(pro))
		for p, pv := range pro {
			_, host, _, err := getHostNameAndGuidFromProcess(pv, platfrom)
			if err != nil {
				return nil, err
			}

			ptn[p] = host

			//if _, ok := dagNodes[host]; !ok {
			//	data, err := orm.K8sContainerInfo(c, host, guid)
			//	if err != nil {
			//		return nil, err
			//	}
			//	dagNodes[host] = map[string]interface{}{
			//		"service": servic,
			//		"type":    "container",
			//		"info":    data,
			//	}
			//}
		}

		sps := v.(map[string]interface{})["spans"].([]interface{})
		lk, err := getDagLink(sps)

		//fmt.Printf("get lk %v", lk)

		if err != nil {
			return nil, err
		}

		//c.Logger.Debugf("dag link len %v", len(lk))

		for k, v := range lk {
			for ck, cv := range v {
				if k != ck {
					if _, ok := dagLinksMap[ptn[k]]; !ok {
						dagLinksMap[ptn[k]] = make(map[string]int)
						dagLinksMapT[ptn[k]] = make(map[string][]string)
					}
					dagLinksMap[ptn[k]][ptn[ck]] += cv
					dagLinksMapT[ptn[k]][ptn[ck]] = append(dagLinksMapT[ptn[k]][ptn[ck]], id)
				}
			}
		}
	}

	//fmt.Printf("daglink map %v", dagLinksMap)

	for k, v := range dagLinksMap {
		for ck, cv := range v {
			if k != ck {
				dagLinks = append(dagLinks, map[string]interface{}{
					"source":   k,
					"target":   ck,
					"value":    cv,
					"traceIds": dagLinksMapT[k][ck],
				})
			}
		}
	}

	return dagLinks, nil
}

func getHostNameAndGuidFromProcess(processes interface{}, platfrom string) (string, string, string, error) {
	var guid, host, service string

	p1, ok := processes.(map[string]interface{})
	if !ok {
		return "", "", "", fmt.Errorf("processes is not map[string]interface{}")
	}

	service = p1["serviceName"].(string)

	p2, ok := p1["tags"].([]interface{})
	if !ok {
		return "", "", "", fmt.Errorf("processes tags is not []interface{}")
	}

	switch platfrom {
	case apis.PlatfromGluenets:
		var hostname string
		for _, v := range p2 {
			if v.(map[string]interface{})["key"].(string) == "hostname" {
				hostname = v.(map[string]interface{})["value"].(string)
			}
		}

		hs := strings.Split(hostname, "_")
		guid = hs[0]
		host = hs[1]
	case apis.PlatfromKubernetes:
		for _, v := range p2 {
			if v.(map[string]interface{})["key"].(string) == "guid" {
				guid = v.(map[string]interface{})["value"].(string)
			}
			if v.(map[string]interface{})["key"].(string) == "hostname" {
				host = v.(map[string]interface{})["value"].(string)
			}
		}
	}

	if guid == "" || host == "" || service == "" {
		return "", "", "", fmt.Errorf("can't find tag guid or host or service name")
	}
	return guid, host, service, nil
}

func getDagLink(spans []interface{}) (map[string]map[string]int, error) {

	var sTp = make(map[string]string)
	var dag = make(map[string]map[string]int)
	for _, s := range spans {
		span := s.(map[string]interface{})

		spanId := span["spanID"].(string)

		processId := span["processID"].(string)

		sTp[spanId] = processId

		parentIdRefer := span["references"].([]interface{})
		for _, p := range parentIdRefer {
			parent := p.(map[string]interface{})["spanID"].(string)
			if _, ok := dag[parent]; !ok {
				dag[parent] = make(map[string]int)
				dag[parent][spanId] = 1
			}
			dag[parent][spanId]++
		}
	}

	var dagInfo = make(map[string]map[string]int)
	for k, v := range dag {
		pParent := sTp[k]
		if _, ok := dagInfo[pParent]; !ok {
			dagInfo[pParent] = make(map[string]int)
		}
		for ck, cv := range v {
			pChild := sTp[ck]
			if _, ok := dagInfo[pParent][pChild]; !ok {
				dagInfo[pParent][pChild] = cv
			}
			dagInfo[pParent][pChild] += cv
		}
	}

	return dagInfo, nil
}
